home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / filter.c < prev    next >
C/C++ Source or Header  |  1996-05-21  |  63KB  |  2,770 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: filter.c,v 4.83 1996/05/21 22:24:59 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      filter.c
  44.  
  45.      This code provides a generalized, flexible way to allow
  46.      piping of data thru filters.  Each filter is passed a structure
  47.      that it will use to hold its static data while it operates on 
  48.      the stream of characters that are passed to it.  After processing
  49.      it will either return or call the next filter in 
  50.      the pipe with any character (or characters) it has ready to go. This
  51.      means some terminal type of filter has to be the last in the 
  52.      chain (i.e., one that writes the passed char someplace, but doesn't
  53.      call another filter).
  54.  
  55.      See below for more details.
  56.  
  57.      The motivation is to handle MIME decoding, richtext conversion, 
  58.      iso_code stripping and anything else that may come down the
  59.      pike (e.g., PEM) in an elegant fashion.  mikes (920811)
  60.  
  61.    TODO:
  62.        reasonable error handling
  63.  
  64.   ====*/
  65.  
  66.  
  67. #include "headers.h"
  68.  
  69.  
  70. /*
  71.  * Internal prototypes
  72.  */
  73. int    gf_so_readc PROTO((unsigned char *));
  74. int    gf_so_writec PROTO((int));
  75. int    gf_sreadc PROTO((unsigned char *));
  76. int    gf_swritec PROTO((int));
  77. int    gf_freadc PROTO((unsigned char *));
  78. int    gf_fwritec PROTO((int));
  79. void    gf_terminal PROTO((FILTER_S *, int));
  80. char   *gf_filter_puts PROTO((char *));
  81. void    gf_filter_eod PROTO((void));
  82. void    gf_error PROTO((char *));
  83. void    gf_8bit_put PROTO((FILTER_S *, int));
  84. int    so_reaquire PROTO((STORE_S *));
  85. int    so_cs_writec PROTO((int, STORE_S *));
  86. int    so_pico_writec PROTO((int, STORE_S *));
  87. int    so_file_writec PROTO((int, STORE_S *));
  88. int    so_cs_readc PROTO((unsigned char *, STORE_S *));
  89. int    so_pico_readc PROTO((unsigned char *, STORE_S *));
  90. int    so_file_readc PROTO((unsigned char *, STORE_S *));
  91. int    so_cs_puts PROTO((STORE_S *, char *));
  92. int    so_pico_puts PROTO((STORE_S *, char *));
  93. int    so_file_puts PROTO((STORE_S *, char *));
  94.  
  95.  
  96. /*
  97.  * GENERALIZED STORAGE FUNCTIONS.  Idea is to allow creation of
  98.  * storage objects that can be written into and read from without
  99.  * the caller knowing if the storage is core or in a file 
  100.  * or whatever.
  101.  */
  102. #define    MSIZE_INIT    8192
  103. #define    MSIZE_INC    4096
  104.  
  105. #ifdef    DOS
  106. #define    NO_PIPE
  107. #define    CRLF_NEWLINES
  108. #define    READ_MODE    "rb"
  109. #define    APPEND_MODE    "a+b"
  110. #else
  111. #ifdef OS2
  112. #define CRLF_NEWLINES
  113. #define READ_MODE    "rb"
  114. #define APPEND_MODE    "a+b"
  115. #else
  116. #define    READ_MODE    "r"
  117. #define    APPEND_MODE    "a+"
  118. #endif
  119. #endif
  120.  
  121.  
  122. /*
  123.  * allocate resources associated with the specified type of
  124.  * storage.  If requesting a named file object, open it for 
  125.  * appending, else just open a temp file.
  126.  *
  127.  * return the filled in storage object
  128.  */
  129. STORE_S *
  130. so_get(source, name, rtype)
  131.     SourceType  source;            /* requested storage type */
  132.     char       *name;            /* file name           */
  133.     int        rtype;            /* file access type      */
  134. {
  135.     STORE_S *so;
  136.     char    *type = (rtype&WRITE_ACCESS) ? APPEND_MODE : READ_MODE;
  137.  
  138.     so = (STORE_S *)fs_get(sizeof(STORE_S));
  139.     memset(so, 0, sizeof(STORE_S));
  140.     so->flags |= rtype;
  141.     
  142.     if(name)                    /* stash the name */
  143.       so->name = cpystr(name);
  144. #ifdef    DOS
  145.     else if(source == TmpFileStar || source == FileStar){
  146.     /*
  147.      * Coerce to TmpFileStar.  The MSC library's "tmpfile()"
  148.      * doesn't observe the "TMP" or "TEMP" environment vars and 
  149.      * always wants to write "\".  This is problematic in shared,
  150.      * networked environments.
  151.      */
  152.     source   = TmpFileStar;
  153.     so->name = temp_nam(NULL, "pi");
  154.     }
  155. #else
  156.     else if(source == TmpFileStar)        /* make one up! */
  157.       so->name = temp_nam(NULL, "pine-tmp");
  158. #endif
  159.  
  160.     so->src = source;
  161.     if(so->src == FileStar || so->src == TmpFileStar){
  162.     so->writec = so_file_writec;
  163.     so->readc  = so_file_readc;
  164.     so->puts   = so_file_puts;
  165.  
  166.     /*
  167.      * The reason for both FileStar and TmpFileStar types is
  168.      * that, named or unnamed, TmpFileStar's are unlinked
  169.      * when the object is given back to the system.  This is
  170.      * useful for keeping us from running out of file pointers as
  171.      * the pointer associated with the object can be temporarily
  172.      * returned to the system without destroying the object.
  173.      * 
  174.      * The programmer is warned to be careful not to assign the
  175.      * TmpFileStar type to any files that are expected to remain
  176.      * after the dust has settled!
  177.      */
  178.     if(so->name){
  179.         if(!(so->txt = (void *)fopen(so->name, type))){
  180.         dprint(1, (debugfile, "so_get error: %s : %s", so->name,
  181.                error_description(errno)));
  182.         fs_give((void **)&so->name);
  183.         fs_give((void **)&so);         /* so freed & set to NULL */
  184.         }
  185.     }
  186.     else{
  187.         if(!(so->txt = (void *)create_tmpfile())){
  188.         dprint(1, (debugfile, "so_get error: tmpfile : %s",
  189.                error_description(errno)));
  190.         fs_give((void **)&so);        /* so freed & set to NULL */
  191.         }
  192.     }
  193.     }
  194.     else if(so->src == PicoText){
  195.     so->writec = so_pico_writec;
  196.     so->readc  = so_pico_readc;
  197.     so->puts   = so_pico_puts;
  198.     if(!(so->txt = pico_get())){
  199.         dprint(1, (debugfile, "so_get error: alloc of pico text space"));
  200.         if(so->name)
  201.           fs_give((void **)&so->name);
  202.         fs_give((void **)&so);        /* so freed & set to NULL */
  203.     }
  204.     }
  205.     else{
  206.     so->writec = so_cs_writec;
  207.     so->readc  = so_cs_readc;
  208.     so->puts   = so_cs_puts;
  209.     so->txt       = (void *)fs_get((size_t) MSIZE_INIT * sizeof(char));
  210.     so->dp       = so->eod = (unsigned char *) so->txt;
  211.     so->eot       = so->dp + MSIZE_INIT;
  212.     memset(so->eod, 0, so->eot - so->eod);
  213.     }
  214.  
  215.     return(so);
  216. }
  217.  
  218.  
  219. /* 
  220.  * so_give - free resources associated with a storage object and then
  221.  *           the object itself.
  222.  */
  223. void
  224. so_give(so)
  225. STORE_S **so;
  226. {
  227.     if(!so)
  228.       return;
  229.  
  230.     if((*so)->src == FileStar || (*so)->src == TmpFileStar){
  231.         if((*so)->txt)
  232.       fclose((FILE *)(*so)->txt);    /* disassociate from storage */
  233.  
  234.     if((*so)->name && (*so)->src == TmpFileStar)
  235.       unlink((*so)->name);        /* really disassociate! */
  236.     }
  237.     else if((*so)->txt && (*so)->src == PicoText)
  238.       pico_give((*so)->txt);
  239.     else if((*so)->txt)
  240.       fs_give((void **)&((*so)->txt));
  241.  
  242.     if((*so)->name)
  243.       fs_give((void **)&((*so)->name));    /* blast the name            */
  244.  
  245.     fs_give((void **)so);        /* release the object        */
  246. }
  247.  
  248.  
  249. /*
  250.  * put a character into the specified storage object, 
  251.  * expanding if neccessary 
  252.  *
  253.  * return 1 on success and 0 on failure
  254.  */
  255. int
  256. so_cs_writec(c, so)
  257.     int      c;
  258.     STORE_S *so;
  259. {
  260.     register unsigned char  ch = (unsigned char) c;
  261.  
  262.     if(so->dp >= so->eot){
  263.     register size_t cur_o  = so->dp - (unsigned char *)so->txt;
  264.     register size_t data_o = so->eod - (unsigned char *)so->txt;
  265.     register size_t size   = (so->eot-(unsigned char *)so->txt)+MSIZE_INC;
  266.     fs_resize(&so->txt, size * sizeof(char));
  267.     so->dp   = (unsigned char *) so->txt + cur_o;
  268.     so->eod  = (unsigned char *) so->txt + data_o;
  269.     so->eot  = (unsigned char *) so->txt + size;
  270.     memset(so->eod, 0, so->eot - so->eod);
  271.     }
  272.  
  273.     *so->dp++ = ch;
  274.     if(so->dp > so->eod)
  275.       so->eod = so->dp;
  276.  
  277.     return(1);
  278. }
  279.  
  280. int
  281. so_pico_writec(c, so)
  282.     int      c;
  283.     STORE_S *so;
  284. {
  285.     unsigned char ch = (unsigned char) c;
  286.  
  287.     return(pico_writec(so->txt, ch));
  288. }
  289.  
  290. int
  291. so_file_writec(c, so)
  292.     int      c;
  293.     STORE_S *so;
  294. {
  295.     unsigned char ch = (unsigned char) c;
  296.     int rv = 0;
  297.  
  298.     if(so->txt || so_reaquire(so))
  299.       do
  300.     rv = fwrite(&ch,sizeof(unsigned char),(size_t)1,(FILE *)so->txt);
  301.       while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
  302.  
  303.     return(rv);
  304. }
  305.  
  306.  
  307. /*
  308.  * get a character from the specified storage object.
  309.  * 
  310.  * return 1 on success and 0 on failure
  311.  */
  312. int
  313. so_cs_readc(c, so)
  314.     unsigned char *c;
  315.     STORE_S       *so;
  316. {
  317.     return((so->dp < so->eod) ? *c = *(so->dp)++, 1 : 0);
  318. }
  319.  
  320. int
  321. so_pico_readc(c, so)
  322.     unsigned char *c;
  323.     STORE_S       *so;
  324. {
  325.     return(pico_readc(so->txt, c));
  326. }
  327.  
  328. int
  329. so_file_readc(c, so)
  330.     unsigned char *c;
  331.     STORE_S       *so;
  332. {
  333.     int rv = 0;
  334.  
  335.     if(so->txt || so_reaquire(so))
  336.       do
  337.     rv = fread(c, sizeof(char), (size_t)1, (FILE *)so->txt);
  338.       while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
  339.  
  340.     return(rv);
  341. }
  342.  
  343.  
  344. /* 
  345.  * write a string into the specified storage object, 
  346.  * expanding if necessary (and cheating if the object 
  347.  * happens to be a file!)
  348.  *
  349.  * return 1 on success and 0 on failure
  350.  */
  351. int
  352. so_cs_puts(so, s)
  353.     STORE_S *so;
  354.     char    *s;
  355. {
  356.     int slen = strlen(s);
  357.  
  358.     if(so->dp + slen >= so->eot){
  359.     register size_t cur_o  = so->dp - (unsigned char *) so->txt;
  360.     register size_t data_o = so->eod - (unsigned char *) so->txt;
  361.     register size_t len   = so->eot - (unsigned char *) so->txt;
  362.     while(len <= cur_o + slen + 1)
  363.       len += MSIZE_INC;        /* need to resize! */
  364.  
  365.     fs_resize(&so->txt, len * sizeof(char));
  366.     so->dp     = (unsigned char *)so->txt + cur_o;
  367.     so->eod     = (unsigned char *)so->txt + data_o;
  368.     so->eot     = (unsigned char *)so->txt + len;
  369.     memset(so->eod, 0, so->eot - so->eod);
  370.     }
  371.  
  372.     memcpy(so->dp, s, slen);
  373.     so->dp += slen;
  374.     if(so->dp > so->eod)
  375.       so->eod = so->dp;
  376.  
  377.     return(1);
  378. }
  379.  
  380. int
  381. so_pico_puts(so, s)
  382.     STORE_S *so;
  383.     char    *s;
  384. {
  385.     return(pico_puts(so->txt, s));
  386. }
  387.  
  388. int
  389. so_file_puts(so, s)
  390.     STORE_S *so;
  391.     char    *s;
  392. {
  393.     int rv = *s ? 0 : 1;
  394.  
  395.     if(!rv && (so->txt || so_reaquire(so)))
  396.       do
  397.     rv = fwrite(s, strlen(s)*sizeof(char), (size_t)1, (FILE *)so->txt);
  398.       while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
  399.  
  400.     return(rv);
  401. }
  402.  
  403.  
  404.  
  405. /*
  406.  * Position the storage object's pointer to the given offset
  407.  * from the start of the object's data.
  408.  */
  409. int
  410. so_seek(so, pos, orig)
  411.     STORE_S *so;
  412.     long     pos;
  413.     int      orig;
  414. {
  415.     if(so->src == CharStar){
  416.     switch(orig){
  417.         case 0 :                /* SEEK_SET */
  418.           return((pos < so->eod - (unsigned char *) so->txt)
  419.               ? so->dp = (unsigned char *)so->txt + pos, 0 : -1);
  420.         case 1 :                /* SEEK_CUR */
  421.           return((pos > 0)
  422.                ? ((pos < so->eod - so->dp) ? so->dp += pos, 0: -1)
  423.                : ((pos < 0)
  424.                ? ((-pos < so->dp - (unsigned char *)so->txt)
  425.                     ? so->dp += pos, 0 : -1)
  426.                : 0));
  427.         case 2 :                /* SEEK_END */
  428.           return((pos < so->eod - (unsigned char *) so->txt)
  429.               ? so->dp = so->eod - pos, 0 : -1);
  430.         default :
  431.           return(-1);
  432.     }
  433.     }
  434.     else if(so->src == PicoText)
  435.       return(pico_seek(so->txt, pos, orig));
  436.     else            /* FileStar or TmpFileStar */
  437.       return((so->txt || so_reaquire(so)) && fseek((FILE *)so->txt,pos,orig));
  438. }
  439.  
  440.  
  441. /*
  442.  * Change the given storage object's size to that specified.  If size
  443.  * is less than the current size, the internal pointer is adjusted and
  444.  * all previous data beyond the given size is lost.
  445.  */
  446. int
  447. so_truncate(so, size)
  448.     STORE_S *so;
  449.     long     size;
  450. {
  451.     if(so->src == CharStar){
  452.     if(so->eod < (unsigned char *) so->txt + size){    /* alloc! */
  453.         unsigned char *newtxt = (unsigned char *) so->txt;
  454.         register size_t len   = so->eot - (unsigned char *) so->txt;
  455.  
  456.         while(len <= size)
  457.           len += MSIZE_INC;        /* need to resize! */
  458.  
  459.         if(len > so->eot - (unsigned char *) newtxt){
  460.         fs_resize((void **) &newtxt, len * sizeof(char));
  461.         so->eot = newtxt + len;
  462.         so->eod = newtxt + (so->eod - (unsigned char *) so->txt);
  463.         memset(so->eod, 0, so->eot - so->eod);
  464.         }
  465.  
  466.         so->eod = newtxt + size;
  467.         so->dp  = newtxt + (so->dp - (unsigned char *) so->txt);
  468.         so->txt = newtxt;
  469.     }
  470.     else if(so->eod > (unsigned char *) so->txt + size){
  471.         if(so->dp > (so->eod = (unsigned char *)so->txt + size))
  472.           so->dp = so->eod;
  473.  
  474.         memset(so->eod, 0, so->eot - so->eod);
  475.     }
  476.     }
  477.     else if(so->src == PicoText)
  478.       fatal("programmer botch: unsupported so_truncate call");
  479.     else            /* FileStar or TmpFileStar */
  480.       return(ftruncate(fileno((FILE *)so->txt), size));
  481. }
  482.  
  483.  
  484. /*
  485.  * so_release - a rather misnamed function.  the idea is to release
  486.  *              what system resources we can (e.g., open files).
  487.  *              while maintaining a reference to it.
  488.  *              it's up to the functions that deal with this object
  489.  *              next to re-aquire those resources.
  490.  */
  491. int
  492. so_release(so)
  493. STORE_S *so;
  494. {
  495.     if(so->txt && so->name && (so->src == FileStar || so->src == TmpFileStar)){
  496.     if(fget_pos((FILE *)so->txt, (fpos_t *)&(so->used)) == 0){
  497.         fclose((FILE *)so->txt);        /* free the handle! */
  498.         so->txt = NULL;
  499.     }
  500.     }
  501.  
  502.     return(1);
  503. }
  504.  
  505.  
  506. /*
  507.  * so_reaquire - get any previously released system resources we
  508.  *               may need for the given storage object.
  509.  *       NOTE: at the moment, only FILE * types of objects are
  510.  *             effected, so it only needs to be called before
  511.  *             references to them.
  512.  *                     
  513.  */
  514. so_reaquire(so)
  515. STORE_S *so;
  516. {
  517.     int   rv = 1;
  518.     char  *type = ((so->flags)&WRITE_ACCESS) ? APPEND_MODE : READ_MODE;
  519.  
  520.     if(!so->txt && (so->src == FileStar || so->src == TmpFileStar)){
  521.     if(!(so->txt=(void *)fopen(so->name, type))){
  522.         q_status_message2(SM_ORDER,3,5, "ERROR reopening %s : %s", so->name,
  523.                 error_description(errno));
  524.         rv = 0;
  525.     }
  526.     else if(fset_pos((FILE *)so->txt, (fpos_t *)&(so->used))){
  527.         q_status_message2(SM_ORDER, 3, 5, "ERROR positioning in %s : %s", 
  528.                 so->name, error_description(errno));
  529.         rv = 0;
  530.     }
  531.     }
  532.  
  533.     return(rv);
  534. }
  535.  
  536.  
  537. /*
  538.  * so_text - return a pointer to the text the store object passed
  539.  */
  540. void *
  541. so_text(so)
  542. STORE_S *so;
  543. {
  544.     return((so) ? so->txt : NULL);
  545. }
  546.  
  547.  
  548. /*
  549.  * END OF GENERALIZE STORAGE FUNCTIONS
  550.  */
  551.  
  552.  
  553. /*
  554.  * Start of filters, pipes and various support functions
  555.  */
  556.  
  557. /*
  558.  * pointer to first function in a pipe, and pointer to last filter
  559.  */
  560. FILTER_S         *gf_master = NULL;
  561. static    gf_io_t   last_filter;
  562. static    char     *gf_error_string;
  563. static    long      gf_byte_count;
  564. static    jmp_buf   gf_error_state;
  565.  
  566.  
  567. /*
  568.  * A list of states used by the various filters.  Reused in many filters.
  569.  */
  570. #define    DFL    0
  571. #define    EQUAL    1
  572. #define    HEX    2
  573. #define    WSPACE    3
  574. #define    CCR    4
  575. #define    CLF    5
  576. #define    CESCP    6
  577. #define    TOKEN    7
  578. #define    LITERAL    8
  579.  
  580.  
  581. /*
  582.  * Macros to reduce function call overhead associated with calling
  583.  * each filter for each byte filtered, and to minimize filter structure
  584.  * dereferences.  NOTE: "queuein" has to do with putting chars into the
  585.  * filter structs data queue.  So, writing at the queuein offset is 
  586.  * what a filter does to pass processed data out of itself.  Ditto for
  587.  * queueout.  This explains the FI --> queueout init stuff below.
  588.  */
  589. #define    GF_QUE_START(F)    (&(F)->queue[0])
  590. #define    GF_QUE_END(F)    (&(F)->queue[GF_MAXBUF - 1])
  591.  
  592. #define    GF_IP_INIT(F)    ip  = (F) ? &(F)->queue[(F)->queuein] : NULL
  593. #define    GF_EIB_INIT(F)    eib = (F) ? GF_QUE_END(F) : NULL
  594. #define    GF_OP_INIT(F)    op  = (F) ? &(F)->queue[(F)->queueout] : NULL
  595. #define    GF_EOB_INIT(F)    eob = (F) ? &(F)->queue[(F)->queuein] : NULL
  596.  
  597. #define    GF_IP_END(F)    (F)->queuein  = ip - GF_QUE_START(F)
  598. #define    GF_OP_END(F)    (F)->queueout = op - GF_QUE_START(F)
  599.  
  600. #define    GF_INIT(FI, FO)    register unsigned char *GF_OP_INIT(FI);     \
  601.             register unsigned char *GF_EOB_INIT(FI); \
  602.             register unsigned char *GF_IP_INIT(FO);  \
  603.             register unsigned char *GF_EIB_INIT(FO);
  604.  
  605. #define    GF_CH_RESET(F)    ((int)(op = eob = GF_QUE_START(F), \
  606.                         (F)->queueout = (F)->queuein = 0))
  607.  
  608. #define    GF_END(FI, FO)    (GF_OP_END(FI), GF_IP_END(FO))
  609.  
  610. #define    GF_FLUSH(F)    ((int)(GF_IP_END(F), (*(F)->f)((F), GF_DATA), \
  611.                    GF_IP_INIT(F), GF_EIB_INIT(F)))
  612.  
  613. #define    GF_PUTC(F, C)    ((int)(*ip++ = (C), (ip >= eib) ? GF_FLUSH(F) : 1))
  614.  
  615. #define    GF_GETC(F, C)    ((op < eob) ? ((int)((C) = *op++), 1) : GF_CH_RESET(F))
  616.  
  617.  
  618. /*
  619.  * Generalized getc and putc routines.  provided here so they don't
  620.  * need to be re-done elsewhere to 
  621.  */
  622.  
  623. /*
  624.  * pointers to objects to be used by the generic getc and putc
  625.  * functions
  626.  */
  627. static struct gf_io_struct {
  628.     FILE          *file;
  629.     char          *txtp;
  630.     unsigned long  n;
  631. } gf_in, gf_out;
  632. static STORE_S *gf_so_in, *gf_so_out;
  633.  
  634.  
  635. /*
  636.  * setup to use and return a pointer to the generic
  637.  * getc function
  638.  */
  639. void
  640. gf_set_readc(gc, txt, len, src)
  641.     gf_io_t       *gc;
  642.     void          *txt;
  643.     unsigned long  len;
  644.     SourceType     src;
  645. {
  646.     gf_in.n = len;
  647.     if(src == FileStar){
  648.     gf_in.file = (FILE *)txt;
  649.     fseek(gf_in.file, 0L, 0);
  650.     *gc = gf_freadc;
  651.     }
  652.     else{
  653.     gf_in.txtp = (char *)txt;
  654.     *gc = gf_sreadc;
  655.     }
  656. }
  657.  
  658.  
  659. /*
  660.  * setup to use and return a pointer to the generic
  661.  * putc function
  662.  */
  663. void
  664. gf_set_writec(pc, txt, len, src)
  665.     gf_io_t       *pc;
  666.     void          *txt;
  667.     unsigned long  len;
  668.     SourceType     src;
  669. {
  670.     gf_out.n = len;
  671.     if(src == FileStar){
  672.     gf_out.file = (FILE *)txt;
  673.     *pc = gf_fwritec;
  674.     }
  675.     else{
  676.     gf_out.txtp = (char *)txt;
  677.     *pc = gf_swritec;
  678.     }
  679. }
  680.  
  681.  
  682. /*
  683.  * setup to use and return a pointer to the generic
  684.  * getc function
  685.  */
  686. void
  687. gf_set_so_readc(gc, so)
  688.     gf_io_t *gc;
  689.     STORE_S *so;
  690. {
  691.     gf_so_in = so;
  692.     *gc      = gf_so_readc;
  693. }
  694.  
  695.  
  696. /*
  697.  * setup to use and return a pointer to the generic
  698.  * putc function
  699.  */
  700. void
  701. gf_set_so_writec(pc, so)
  702.     gf_io_t *pc;
  703.     STORE_S *so;
  704. {
  705.     gf_so_out = so;
  706.     *pc       = gf_so_writec;
  707. }
  708.  
  709.  
  710. /*
  711.  * put the character to the object previously defined
  712.  */
  713. int
  714. gf_so_writec(c)
  715. int c;
  716. {
  717.     return(so_writec(c, gf_so_out));
  718. }
  719.  
  720.  
  721. /*
  722.  * get a character from an object previously defined
  723.  */
  724. int
  725. gf_so_readc(c)
  726. unsigned char *c;
  727. {
  728.     return(so_readc(c, gf_so_in));
  729. }
  730.  
  731.  
  732. /* get a character from a file */
  733. /* assumes gf_out struct is filled in */
  734. int
  735. gf_freadc(c)
  736. unsigned char *c;
  737. {
  738.     int rv = 0;
  739.  
  740.     do
  741.       rv = fread(c, sizeof(unsigned char), (size_t)1, gf_in.file);
  742.     while(!rv && ferror(gf_in.file) && errno == EINTR);
  743.  
  744.     return(rv);
  745. }
  746.  
  747.  
  748. /* put a character to a file */
  749. /* assumes gf_out struct is filled in */
  750. int
  751. gf_fwritec(c)
  752.     int c;
  753. {
  754.     unsigned char ch = (unsigned char)c;
  755.     int rv = 0;
  756.  
  757.     do
  758.       rv = fwrite(&ch, sizeof(unsigned char), (size_t)1, gf_out.file);
  759.     while(!rv && ferror(gf_out.file) && errno == EINTR);
  760.  
  761.     return(rv);
  762. }
  763.  
  764.  
  765. /* get a character from a string, return nonzero if things OK */
  766. /* assumes gf_out struct is filled in */
  767. int
  768. gf_sreadc(c)
  769. unsigned char *c;
  770. {
  771.     return((gf_in.n) ? *c = *(gf_in.txtp)++, gf_in.n-- : 0);
  772. }
  773.  
  774.  
  775. /* put a character into a string, return nonzero if things OK */
  776. /* assumes gf_out struct is filled in */
  777. int
  778. gf_swritec(c)
  779.     int c;
  780. {
  781.     return((gf_out.n) ? *(gf_out.txtp)++ = c, gf_out.n-- : 0);
  782. }
  783.  
  784.  
  785. /*
  786.  * output the given string with the given function
  787.  */
  788. int
  789. gf_puts(s, pc)
  790.     register char *s;
  791.     gf_io_t        pc;
  792. {
  793.     while(*s != '\0')
  794.       if(!(*pc)((unsigned char)*s++))
  795.     return(0);        /* ERROR putting char ! */
  796.  
  797.     return(1);
  798. }
  799.  
  800.  
  801. /*
  802.  * Start of generalized filter routines
  803.  */
  804.  
  805. /* 
  806.  * initializing function to make sure list of filters is empty.
  807.  */
  808. void
  809. gf_filter_init()
  810. {
  811.     FILTER_S *flt, *fltn = gf_master;
  812.  
  813.     while((flt = fltn) != NULL){    /* free list of old filters */
  814.     fltn = flt->next;
  815.     fs_give((void **)&flt);
  816.     }
  817.  
  818.     gf_master = NULL;
  819.     gf_error_string = NULL;        /* clear previous errors */
  820.     gf_byte_count = 0L;            /* reset counter */
  821. }
  822.  
  823.  
  824.  
  825. /*
  826.  * link the given filter into the filter chain
  827.  */
  828. gf_link_filter(f)
  829.     filter_t f;
  830. {
  831.     FILTER_S *new, *tail;
  832.  
  833.     new = (FILTER_S *)fs_get(sizeof(FILTER_S));
  834.     memset(new, 0, sizeof(FILTER_S));
  835.  
  836.     new->f = f;                /* set the function pointer     */
  837.     (*f)(new, GF_RESET);        /* have it setup initial state  */
  838.  
  839.     if(tail = gf_master){        /* or add it to end of existing  */
  840.     while(tail->next)        /* list  */
  841.       tail = tail->next;
  842.  
  843.     tail->next = new;
  844.     }
  845.     else                /* attach new struct to list    */
  846.       gf_master = new;            /* start a new list */
  847. }
  848.  
  849.  
  850. /*
  851.  * terminal filter, doesn't call any other filters, typically just does
  852.  * something with the output
  853.  */
  854. void
  855. gf_terminal(f, flg)
  856.     FILTER_S *f;
  857.     int       flg;
  858. {
  859.     if(flg == GF_DATA){
  860.     GF_INIT(f, f);
  861.  
  862.     while(op < eob)
  863.       if((*last_filter)(*op++) <= 0) /* generic terminal filter */
  864.         gf_error(errno ? error_description(errno) : "Error writing pipe");
  865.  
  866.     GF_CH_RESET(f);
  867.     }
  868.     else if(flg == GF_RESET)
  869.       errno = 0;            /* prepare for problems */
  870. }
  871.  
  872.  
  873. /*
  874.  * set some outside gf_io_t function to the terminal function 
  875.  * for example: a function to write a char to a file or into a buffer
  876.  */
  877. void
  878. gf_set_terminal(f)            /* function to set generic filter */
  879.     gf_io_t f;
  880. {
  881.     last_filter = f;
  882. }
  883.  
  884.  
  885. /*
  886.  * common function for filter's to make it known that an error
  887.  * has occurred.  Jumps back to gf_pipe with error message.
  888.  */
  889. void
  890. gf_error(s)
  891.     char *s;
  892. {
  893.     /* let the user know the error passed in s */
  894.     gf_error_string = s;
  895.     longjmp(gf_error_state, 1);
  896. }
  897.  
  898.  
  899. /*
  900.  * The routine that shoves each byte through the chain of
  901.  * filters.  It sets up error handling, and the terminal function.
  902.  * Then loops getting bytes with the given function, and passing
  903.  * it on to the first filter in the chain.
  904.  */
  905. char *
  906. gf_pipe(gc, pc)
  907.     gf_io_t gc, pc;            /* how to get a character */
  908. {
  909.     unsigned char c;
  910.  
  911. #ifdef    DOS
  912.     MoveCursor(0, 1);
  913.     StartInverse();
  914. #endif
  915.  
  916.     dprint(4, (debugfile, "-- gf_pipe: "));
  917.  
  918.     /*
  919.      * set up for any errors a filter may encounter
  920.      */
  921.     if(setjmp(gf_error_state)){
  922. #ifdef    DOS
  923.     ibmputc(' ');
  924.     EndInverse();
  925. #endif
  926.     dprint(4, (debugfile, "ERROR: %s\n",
  927.            gf_error_string ? gf_error_string : "NULL"));
  928.     return(gf_error_string);     /*  */
  929.     }
  930.  
  931.     /*
  932.      * set and link in the terminal filter
  933.      */
  934.     gf_set_terminal(pc);
  935.     gf_link_filter(gf_terminal);
  936.  
  937.     /* 
  938.      * while there are chars to process, send them thru the pipe.
  939.      * NOTE: it's necessary to enclose the loop below in a block
  940.      * as the GF_INIT macro calls some automatic var's into
  941.      * existence.  It can't be placed at the start of gf_pipe
  942.      * because its useful for us to be called without filters loaded
  943.      * when we're just being used to copy bytes between storage
  944.      * objects.
  945.      */
  946.     {
  947.     GF_INIT(gf_master, gf_master);
  948.  
  949.     while((*gc)(&c)){
  950.         gf_byte_count++;
  951. #ifdef    DOS
  952.         if(!(gf_byte_count & 0x3ff))
  953. #ifdef    _WINDOWS
  954.           /* Under windows we yeild to allow event processing.
  955.            * Progress display is handled throught the alarm()
  956.            * mechinism.
  957.            */
  958.           mswin_yeild ();
  959. #else
  960.           /* Poor PC still needs spinning bar */
  961.           ibmputc("/-\\|"[((int) gf_byte_count >> 10) % 4]);
  962.           MoveCursor(0, 1);
  963. #endif
  964. #endif
  965.  
  966.         GF_PUTC(gf_master, c & 0xff);
  967.     }
  968.  
  969.     /*
  970.      * toss an end-of-data marker down the pipe to give filters
  971.      * that have any buffered data the opportunity to dump it
  972.      */
  973.     GF_FLUSH(gf_master);
  974.     (*gf_master->f)(gf_master, GF_EOD);
  975.     }
  976.  
  977. #ifdef    DOS
  978.     ibmputc(' ');
  979.     EndInverse();
  980. #endif
  981.  
  982.     dprint(1, (debugfile, "done.\n"));
  983.     return(NULL);            /* everything went OK */
  984. }
  985.  
  986.  
  987. /*
  988.  * return the number of bytes piped so far
  989.  */
  990. long
  991. gf_bytes_piped()
  992. {
  993.     return(gf_byte_count);
  994. }
  995.  
  996.  
  997. /*
  998.  * filter the given input with the given command
  999.  *
  1000.  *  Args: cmd -- command string to execute
  1001.  *    prepend -- string to prepend to filtered input
  1002.  *    source_so -- storage object containing data to be filtered
  1003.  *    pc -- function to write filtered output with
  1004.  *    aux_filters -- additional filters to pass data thru after "cmd"
  1005.  *
  1006.  *  Returns: NULL on sucess, reason for failure (not alloc'd!) on error
  1007.  */
  1008. char *
  1009. gf_filter(cmd, prepend, source_so, pc, aux_filters)
  1010.     char     *cmd, *prepend;
  1011.     STORE_S  *source_so;
  1012.     gf_io_t   pc;
  1013.     filter_t *aux_filters;
  1014. {
  1015.     unsigned char c;
  1016.     int         flags;
  1017.     char   *errstr = NULL, buf[MAILTMPLEN], *rfile = NULL;
  1018.     PIPE_S *fpipe;
  1019.  
  1020.     gf_filter_init();
  1021.     while(aux_filters && *aux_filters)
  1022.       gf_link_filter(*aux_filters++);
  1023.  
  1024.     gf_set_terminal(pc);
  1025.     gf_link_filter(gf_terminal);
  1026.  
  1027.     /*
  1028.      * Spawn filter feeding it data, and reading what it writes.
  1029.      */
  1030.     so_seek(source_so, 0L, 0);
  1031. #ifdef    NO_PIPE
  1032.     /*
  1033.      * When there're no pipes for IPC, use an output file to collect
  1034.      * the result...
  1035.      */
  1036.     flags = PIPE_WRITE | PIPE_NOSHELL | PIPE_RESET;
  1037.     rfile = temp_nam(NULL, "pf");
  1038. #else
  1039.     flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL | PIPE_RESET;
  1040. #endif
  1041.  
  1042.     if(fpipe = open_system_pipe(cmd, rfile ? &rfile : NULL, NULL, flags)){
  1043. #ifdef    NO_PIPE
  1044.     if(prepend && (fputs(prepend, fpipe->out.f) == EOF
  1045.                || fputc('\n', fpipe->out.f) == EOF))
  1046.       errstr = error_description(errno);
  1047.  
  1048.     /*
  1049.      * Write the output, and deal with the result later...
  1050.      */
  1051.     while(!errstr && so_readc(&c, source_so))
  1052.       if(fputc(c, fpipe->out.f) == EOF)
  1053.         errstr = error_description(errno);
  1054. #else
  1055. #ifdef    NON_BLOCKING_IO
  1056.     int     n;
  1057.  
  1058.     if(fcntl(fileno(fpipe->in.f), F_SETFL, NON_BLOCKING_IO) == -1)
  1059.       errstr = "Can't set up non-blocking IO";
  1060.  
  1061.     if(prepend && (fputs(prepend, fpipe->out.f) == EOF
  1062.                || fputc('\n', fpipe->out.f) == EOF))
  1063.       errstr = error_description(errno);
  1064.  
  1065.     while(!errstr){
  1066.         /* if the pipe can't hold a K we're sunk (too bad PIPE_MAX
  1067.          * isn't ubiquitous ;).
  1068.          */
  1069.         for(n = 0; !errstr && fpipe->out.f && n < 1024; n++)
  1070.           if(!so_readc(&c, source_so)){
  1071.           fclose(fpipe->out.f);
  1072.           fpipe->out.f = NULL;
  1073.           }
  1074.           else if(fputc(c, fpipe->out.f) == EOF)
  1075.         errstr = error_description(errno);
  1076.  
  1077.         /*
  1078.          * Note: We clear errno here and test below, before ferror,
  1079.          *         because *some* stdio implementations consider
  1080.          *         EAGAIN and EWOULDBLOCK equivalent to EOF...
  1081.          */
  1082.         errno = 0;
  1083.  
  1084.         while(!errstr && fgets(buf, MAILTMPLEN, fpipe->in.f))
  1085.           errstr = gf_filter_puts(buf);
  1086.  
  1087.         /* then fgets failed! */
  1088.         if(!errstr && !(errno == EAGAIN || errno == EWOULDBLOCK)){
  1089.         if(feof(fpipe->in.f))        /* nothing else interesting! */
  1090.           break;            
  1091.         else if(ferror(fpipe->in.f))    /* bummer. */
  1092.           errstr = error_description(errno);
  1093.         }
  1094.     }
  1095. #else
  1096.     if(prepend && (fputs(prepend, fpipe->out.f) == EOF
  1097.                || fputc('\n', fpipe->out.f) == EOF))
  1098.       errstr = error_description(errno);
  1099.  
  1100.     /*
  1101.      * Well, do the best we can, and hope the pipe we're writing
  1102.      * doesn't fill up before we start reading...
  1103.      */
  1104.     while(!errstr && so_readc(&c, source_so))
  1105.       if(fputc(c, fpipe->out.f) == EOF)
  1106.         errstr = error_description(errno);
  1107.  
  1108.     fclose(fpipe->out.f);
  1109.     fpipe->out.f = NULL;
  1110.     while(!errstr && fgets(buf, MAILTMPLEN, fpipe->in.f))
  1111.       errstr = gf_filter_puts(buf);
  1112. #endif /* NON_BLOCKING */
  1113. #endif /* NO_PIPE */
  1114.  
  1115.     gf_filter_eod();
  1116.  
  1117.     if(close_system_pipe(&fpipe) && !errstr)
  1118.       errstr = "Pipe command returned error.";
  1119.  
  1120. #ifdef    NO_PIPE
  1121.     /*
  1122.      * retrieve filters result...
  1123.      */
  1124.     {
  1125.         FILE *fp;
  1126.         if(fp = fopen(rfile, "rb")){
  1127.         while(!errstr && fgets(buf, MAILTMPLEN, fp))
  1128.           errstr = gf_filter_puts(buf);
  1129.  
  1130.         fclose(fp);
  1131.         }
  1132.  
  1133.         fs_give((void **)&rfile);
  1134.     }
  1135. #endif
  1136.     }
  1137.  
  1138.     return(errstr);
  1139. }
  1140.  
  1141.  
  1142. /*
  1143.  * gf_filter_puts - write the given string down the filter's pipe
  1144.  */
  1145. char *
  1146. gf_filter_puts(s)
  1147.     register char *s;
  1148. {
  1149.     GF_INIT(gf_master, gf_master);
  1150.  
  1151.     /*
  1152.      * set up for any errors a filter may encounter
  1153.      */
  1154.     if(setjmp(gf_error_state)){
  1155.     dprint(4, (debugfile, "ERROR: gf_filter_puts: %s\n",
  1156.            gf_error_string ? gf_error_string : "NULL"));
  1157.     return(gf_error_string);
  1158.     }
  1159.  
  1160.     while(*s)
  1161.       GF_PUTC(gf_master, (*s++) & 0xff);
  1162.  
  1163.     GF_END(gf_master, gf_master);
  1164.     return(NULL);
  1165. }
  1166.  
  1167.  
  1168. /*
  1169.  * gf_filter_eod - flush pending data filter's input queue and deliver
  1170.  *           the GF_EOD marker.
  1171.  */
  1172. void
  1173. gf_filter_eod()
  1174. {
  1175.     GF_INIT(gf_master, gf_master);
  1176.     GF_FLUSH(gf_master);
  1177.     (*gf_master->f)(gf_master, GF_EOD);
  1178. }
  1179.  
  1180.  
  1181.  
  1182.  
  1183. /*
  1184.  * END OF PIPE SUPPORT ROUTINES, BEGINNING OF FILTERS
  1185.  *
  1186.  * Filters MUST use the specified interface (pointer to filter
  1187.  * structure, the unsigned character buffer in that struct, and a
  1188.  * cmd flag), and pass each resulting octet to the next filter in the
  1189.  * chain.  Only the terminal filter need not call another filter.
  1190.  * As a result, filters share a pretty general structure.
  1191.  * Typically three main conditionals separate initialization from
  1192.  * data from end-of-data command processing.
  1193.  * 
  1194.  * Lastly, being character-at-a-time, they're a little more complex
  1195.  * to write than filters operating on buffers because some state
  1196.  * must typically be kept between characters.  However, for a
  1197.  * little bit of complexity here, much convenience is gained later
  1198.  * as they can be arbitrarily chained together at run time and
  1199.  * consume few resources (especially memory or disk) as they work.
  1200.  * (NOTE 951005: even less cpu now that data between filters is passed
  1201.  *  via a vector.)
  1202.  *
  1203.  * A few notes about implementing filters:
  1204.  *
  1205.  *  - A generic filter template looks like:
  1206.  *
  1207.  *    void
  1208.  *    gf_xxx_filter(f, flg)
  1209.  *        FILTER_S *f;
  1210.  *        int       flg;
  1211.  *    {
  1212.  *      GF_INIT(f, f->next);        // def's var's to speed queue drain
  1213.  *
  1214.  *        if(flg == GF_DATA){
  1215.  *          register unsigned char c;
  1216.  *
  1217.  *          while(GF_GETC(f, c)){    // macro taking data off input queue
  1218.  *              // operate on c and pass it on here
  1219.  *                GF_PUTC(f->next, c);    // macro writing output queue
  1220.  *          }
  1221.  *
  1222.  *          GF_END(f, f->next);    // macro to sync pointers/offsets
  1223.  *          //WARNING: DO NOT RETURN BEFORE ALL INCOMING DATA'S PROCESSED
  1224.  *        }
  1225.  *        else if(flg == GF_EOD){
  1226.  *            // process any buffered data here and pass it on
  1227.  *          GF_FLUSH(f->next);    // flush pending data to next filter
  1228.  *            (*f->next->f)(f->next, GF_EOD);
  1229.  *        }
  1230.  *        else if(flg == GF_RESET){
  1231.  *            // initialize any data in the struct here
  1232.  *        }
  1233.  *    }
  1234.  *
  1235.  *  - Any free storage allocated during initialization (typically tied
  1236.  *    to the "line" pointer in FILTER_S) is the filter's responsibility
  1237.  *    to clean up when the GF_EOD command comes through.
  1238.  *
  1239.  *  - Filter's must pass GF_EOD they receive on to the next
  1240.  *    filter in the chain so it has the opportunity to flush
  1241.  *    any buffered data.
  1242.  *
  1243.  *  - All filters expect NVT end-of-lines.  The idea is to prepend
  1244.  *    or append either the gf_local_nvtnl or gf_nvtnl_local 
  1245.  *    os-dependant filters to the data on the appropriate end of the
  1246.  *    pipe for the task at hand.
  1247.  *
  1248.  *  - NOTE: As of 951004, filters no longer take their input as a single
  1249.  *    char argument, but rather get data to operate on via a vector
  1250.  *    representing the input queue in the FILTER_S structure.
  1251.  *
  1252.  */
  1253.  
  1254.  
  1255.  
  1256. /*
  1257.  * BASE64 TO BINARY encoding and decoding routines below
  1258.  */
  1259.  
  1260.  
  1261. /*
  1262.  * BINARY to BASE64 filter (encoding described in rfc1341)
  1263.  */
  1264. void
  1265. gf_binary_b64(f, flg)
  1266.     FILTER_S *f;
  1267.     int       flg;
  1268. {
  1269.     static char *v =
  1270.             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  1271.     GF_INIT(f, f->next);
  1272.  
  1273.     if(flg == GF_DATA){
  1274.     register unsigned char c;
  1275.     register unsigned char t = f->t;
  1276.     register long n = f->n;
  1277.  
  1278.     while(GF_GETC(f, c)){
  1279.  
  1280.         switch(n++){
  1281.           case 0 : case 3 : case 6 : case 9 : case 12: case 15: case 18:
  1282.           case 21: case 24: case 27: case 30: case 33: case 36: case 39:
  1283.           case 42: case 45:
  1284.         GF_PUTC(f->next, v[c >> 2]);
  1285.                     /* byte 1: high 6 bits (1) */
  1286.         t = c << 4;        /* remember high 2 bits for next */
  1287.         break;
  1288.  
  1289.           case 1 : case 4 : case 7 : case 10: case 13: case 16: case 19:
  1290.           case 22: case 25: case 28: case 31: case 34: case 37: case 40:
  1291.           case 43:
  1292.         GF_PUTC(f->next, v[(t|(c>>4)) & 0x3f]);
  1293.         t = c << 2;
  1294.         break;
  1295.  
  1296.           case 2 : case 5 : case 8 : case 11: case 14: case 17: case 20:
  1297.           case 23: case 26: case 29: case 32: case 35: case 38: case 41:
  1298.           case 44:
  1299.         GF_PUTC(f->next, v[(t|(c >> 6)) & 0x3f]);
  1300.         GF_PUTC(f->next, v[c & 0x3f]);
  1301.         break;
  1302.         }
  1303.  
  1304.         if(n == 45){            /* start a new line? */
  1305.         GF_PUTC(f->next, '\015');
  1306.         GF_PUTC(f->next, '\012');
  1307.         n = 0L;
  1308.         }
  1309.     }
  1310.  
  1311.     f->n = n;
  1312.     f->t = t;
  1313.     GF_END(f, f->next);
  1314.     }
  1315.     else if(flg == GF_EOD){        /* no more data */
  1316.     switch (f->n % 3) {        /* handle trailing bytes */
  1317.       case 0:            /* no trailing bytes */
  1318.         break;
  1319.  
  1320.       case 1:
  1321.         GF_PUTC(f->next, v[(f->t) & 0x3f]);
  1322.         GF_PUTC(f->next, '=');    /* byte 3 */
  1323.         GF_PUTC(f->next, '=');    /* byte 4 */
  1324.         break;
  1325.  
  1326.       case 2:
  1327.         GF_PUTC(f->next, v[(f->t) & 0x3f]);
  1328.         GF_PUTC(f->next, '=');    /* byte 4 */
  1329.         break;
  1330.     }
  1331.  
  1332.     GF_FLUSH(f->next);
  1333.     (*f->next->f)(f->next, GF_EOD);
  1334.     }
  1335.     else if(flg == GF_RESET){
  1336.     dprint(9, (debugfile, "-- gf_reset binary_b64\n"));
  1337.     f->n = 0L;
  1338.     }
  1339. }
  1340.  
  1341.  
  1342.  
  1343. /*
  1344.  * BASE64 to BINARY filter (encoding described in rfc1341)
  1345.  */
  1346. void
  1347. gf_b64_binary(f, flg)
  1348.     FILTER_S *f;
  1349.     int       flg;
  1350. {
  1351.     static char v[] = {65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
  1352.                65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
  1353.                65,65,65,65,65,65,65,65,65,65,65,62,65,65,65,63,
  1354.                52,53,54,55,56,57,58,59,60,61,62,65,65,64,65,65,
  1355.                65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
  1356.                15,16,17,18,19,20,21,22,23,24,25,65,65,65,65,65,
  1357.                65,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
  1358.                41,42,43,44,45,46,47,48,49,50,51,65,65,65,65,65};
  1359.     GF_INIT(f, f->next);
  1360.  
  1361.     if(flg == GF_DATA){
  1362.     register unsigned char c;
  1363.     register unsigned char t = f->t;
  1364.     register int n = (int) f->n;
  1365.     register int state = f->f1;
  1366.  
  1367.     while(GF_GETC(f, c)){
  1368.  
  1369.         if(state){
  1370.         state = 0;
  1371.         if (c != '=') {
  1372.             gf_error("Illegal '=' in base64 text");
  1373.             /* NO RETURN */
  1374.         }
  1375.         }
  1376.  
  1377.         /* in range, and a valid value? */
  1378.         if((c & ~0x7f) || (c = v[c]) > 63){
  1379.         if(c == 64){
  1380.             switch (n++) {    /* check quantum position */
  1381.               case 2:
  1382.             state++;    /* expect an equal as next char */
  1383.             break;
  1384.  
  1385.               case 3:
  1386.             n = 0L;        /* restart quantum */
  1387.             break;
  1388.  
  1389.               default:        /* impossible quantum position */
  1390.             gf_error("Internal base64 decoder error");
  1391.             /* NO RETURN */
  1392.             }
  1393.         }
  1394.         }
  1395.         else{
  1396.         switch (n++) {        /* install based on quantum position */
  1397.           case 0:        /* byte 1: high 6 bits */
  1398.             t = c << 2;
  1399.             break;
  1400.  
  1401.           case 1:        /* byte 1: low 2 bits */
  1402.             GF_PUTC(f->next, (t|(c >> 4)));
  1403.             t = c << 4;        /* byte 2: high 4 bits */
  1404.             break;
  1405.  
  1406.           case 2:        /* byte 2: low 4 bits */
  1407.             GF_PUTC(f->next, (t|(c >> 2)));
  1408.             t = c << 6;        /* byte 3: high 2 bits */
  1409.             break;
  1410.  
  1411.           case 3:
  1412.             GF_PUTC(f->next, t | c);
  1413.             n = 0L;        /* reinitialize mechanism */
  1414.             break;
  1415.         }
  1416.         }
  1417.     }
  1418.  
  1419.     f->f1 = state;
  1420.     f->t = t;
  1421.     f->n = n;
  1422.     GF_END(f, f->next);
  1423.     }
  1424.     else if(flg == GF_EOD){
  1425.     GF_FLUSH(f->next);
  1426.     (*f->next->f)(f->next, GF_EOD);
  1427.     }
  1428.     else if(flg == GF_RESET){
  1429.     dprint(9, (debugfile, "-- gf_reset b64_binary\n"));
  1430.     f->n  = 0L;            /* quantum position */
  1431.     f->f1 = 0;            /* state holder: equal seen? */
  1432.     }
  1433. }
  1434.  
  1435.  
  1436.  
  1437.  
  1438. /*
  1439.  * QUOTED-PRINTABLE ENCODING AND DECODING filters below.
  1440.  * encoding described in rfc1341
  1441.  */
  1442.  
  1443. #define    GF_MAXLINE    80        /* good buffer size */
  1444.  
  1445. /*
  1446.  * default action for QUOTED-PRINTABLE to 8BIT decoder
  1447.  */
  1448. #define    GF_QP_DEFAULT(f, c)    { \
  1449.                     if((c) == ' '){ \
  1450.                     state = WSPACE; \
  1451.                         /* reset white space! */ \
  1452.                     (f)->linep = (f)->line; \
  1453.                     *((f)->linep)++ = ' '; \
  1454.                     } \
  1455.                     else if((c) == '='){ \
  1456.                     state = EQUAL; \
  1457.                     } \
  1458.                     else \
  1459.                       GF_PUTC((f)->next, (c)); \
  1460.                 }
  1461.  
  1462.  
  1463. /*
  1464.  * QUOTED-PRINTABLE to 8BIT filter
  1465.  */
  1466. void
  1467. gf_qp_8bit(f, flg)
  1468.     FILTER_S *f;
  1469.     int       flg;
  1470. {
  1471.     GF_INIT(f, f->next);
  1472.  
  1473.     if(flg == GF_DATA){
  1474.     register unsigned char c;
  1475.     register int state = f->f1;
  1476.  
  1477.     while(GF_GETC(f, c)){
  1478.  
  1479.         switch(state){
  1480.           case DFL :        /* default case */
  1481.           default:
  1482.         GF_QP_DEFAULT(f, c);
  1483.         break;
  1484.  
  1485.           case CCR    :        /* non-significant space */
  1486.         state = DFL;
  1487.         if(c == '\012')
  1488.           continue;        /* go on to next char */
  1489.  
  1490.         GF_QP_DEFAULT(f, c);
  1491.         break;
  1492.  
  1493.           case EQUAL  :
  1494.         if(c == '\015'){    /* "=\015" is a soft EOL */
  1495.             state = CCR;
  1496.             break;
  1497.         }
  1498.  
  1499.         if(c == '='){        /* compatibility clause for old guys */
  1500.             GF_PUTC(f->next, '=');
  1501.             state = DFL;
  1502.             break;
  1503.         }
  1504.  
  1505.         if(!isxdigit((unsigned char)c)){    /* must be hex! */
  1506.             fs_give((void **)&(f->line));
  1507.             gf_error("Non-hexadecimal character in QP encoding");
  1508.             /* NO RETURN */
  1509.         }
  1510.  
  1511.         if (isdigit ((unsigned char)c)) 
  1512.           f->t = c - '0';
  1513.         else
  1514.           f->t = c - (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
  1515.  
  1516.         state = HEX;
  1517.         break;
  1518.  
  1519.           case HEX :
  1520.         state = DFL;
  1521.         if(!isxdigit((unsigned char)c)){    /* must be hex! */
  1522.             fs_give((void **)&(f->line));
  1523.             gf_error("Non-hexadecimal character in QP encoding");
  1524.             /* NO RETURN */
  1525.         }
  1526.  
  1527.         if (isdigit((unsigned char)c)) 
  1528.           c -= '0';
  1529.         else
  1530.           c -= (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
  1531.  
  1532.         GF_PUTC(f->next, c + (f->t << 4));
  1533.         break;
  1534.  
  1535.           case WSPACE :
  1536.         if(c == ' '){        /* toss it in with other spaces */
  1537.             if(f->linep - f->line < GF_MAXLINE)
  1538.               *(f->linep)++ = ' ';
  1539.             break;
  1540.         }
  1541.  
  1542.         state = DFL;
  1543.         if(c == '\015'){    /* not our white space! */
  1544.             f->linep = f->line;    /* reset buffer */
  1545.             GF_PUTC(f->next, '\015');
  1546.             break;
  1547.         }
  1548.  
  1549.         /* the spaces are ours, write 'em */
  1550.         f->n = f->linep - f->line;
  1551.         while((f->n)--)
  1552.           GF_PUTC(f->next, ' ');
  1553.  
  1554.         GF_QP_DEFAULT(f, c);    /* take care of 'c' in default way */
  1555.         break;
  1556.         }
  1557.     }
  1558.  
  1559.     f->f1 = state;
  1560.     GF_END(f, f->next);
  1561.     }
  1562.     else if(flg == GF_EOD){
  1563.     fs_give((void **)&(f->line));
  1564.     GF_FLUSH(f->next);
  1565.     (*f->next->f)(f->next, GF_EOD);
  1566.     }
  1567.     else if(flg == GF_RESET){
  1568.     dprint(9, (debugfile, "-- gf_reset qp_8bit\n"));
  1569.     f->f1 = DFL;
  1570.     f->linep = f->line = (char *)fs_get(GF_MAXLINE * sizeof(char));
  1571.     }
  1572. }
  1573.  
  1574.  
  1575.  
  1576. /*
  1577.  * USEFUL MACROS TO HELP WITH QP ENCODING
  1578.  */
  1579.  
  1580. #define    QP_MAXL    75            /* 76th place only for continuation */
  1581.  
  1582. /*
  1583.  * Macro to test and wrap long quoted printable lines
  1584.  */
  1585. #define    GF_8BIT_WRAP(f)        { \
  1586.                     GF_PUTC((f)->next, '='); \
  1587.                     GF_PUTC((f)->next, '\015'); \
  1588.                     GF_PUTC((f)->next, '\012'); \
  1589.                 }
  1590.  
  1591. /*
  1592.  * write a quoted octet in QUOTED-PRINTABLE encoding, adding soft
  1593.  * line break if needed.
  1594.  */
  1595. #define    GF_8BIT_PUT_QUOTE(f, c)    { \
  1596.                     if(((f)->n += 3) > QP_MAXL){ \
  1597.                     GF_8BIT_WRAP(f); \
  1598.                     (f)->n = 3;    /* set line count */ \
  1599.                     } \
  1600.                     GF_PUTC((f)->next, '='); \
  1601.                         /* high order 4 bits */ \
  1602.                     GF_PUTC((f)->next, hex[(c) >> 4]); \
  1603.                         /* low order 4 bits */ \
  1604.                     GF_PUTC((f)->next, hex[(c) & 0xf]); \
  1605.                 }
  1606.  
  1607. /*
  1608.  * just write an ordinary octet in QUOTED-PRINTABLE, wrapping line
  1609.  * if needed.
  1610.  */
  1611. #define    GF_8BIT_PUT(f, c)    { \
  1612.                      if((++(f->n)) > QP_MAXL){ \
  1613.                      GF_8BIT_WRAP(f); \
  1614.                      f->n = 1L; \
  1615.                      } \
  1616.                      if(f->n == 1L && c == '.'){ \
  1617.                      GF_8BIT_PUT_QUOTE(f, c); \
  1618.                      f->n = 3; \
  1619.                      } \
  1620.                      else \
  1621.                        GF_PUTC(f->next, c); \
  1622.                 }
  1623.  
  1624.  
  1625. /*
  1626.  * default action for 8bit to quoted printable encoder
  1627.  */
  1628. #define    GF_8BIT_DEFAULT(f, c)    if((c) == ' '){ \
  1629.                     state = WSPACE; \
  1630.                 } \
  1631.                 else if(c == '\015'){ \
  1632.                     state = CCR; \
  1633.                 } \
  1634.                 else if(iscntrl(c & 0x7f) || (c == 0x7f) \
  1635.                     || (c & 0x80) || (c == '=')){ \
  1636.                     GF_8BIT_PUT_QUOTE(f, c); \
  1637.                 } \
  1638.                 else{ \
  1639.                   GF_8BIT_PUT(f, c); \
  1640.                 }
  1641.  
  1642.  
  1643. /*
  1644.  * 8BIT to QUOTED-PRINTABLE filter
  1645.  */
  1646. void
  1647. gf_8bit_qp(f, flg)
  1648.     FILTER_S *f;
  1649.     int       flg;
  1650. {
  1651.     static char *hex = "0123456789ABCDEF";
  1652.     short dummy_dots = 0, dummy_dmap = 1;
  1653.     GF_INIT(f, f->next);
  1654.  
  1655.     if(flg == GF_DATA){
  1656.     register unsigned char c;
  1657.     register int state = f->f1;
  1658.  
  1659.     while(GF_GETC(f, c)){
  1660.  
  1661.         /* keep track of "^JFrom " */
  1662.         Find_Froms(f->t, dummy_dots, f->f2, dummy_dmap, c);
  1663.  
  1664.         switch(state){
  1665.           case DFL :        /* handle ordinary case */
  1666.         GF_8BIT_DEFAULT(f, c);
  1667.         break;
  1668.  
  1669.           case CCR :        /* true line break? */
  1670.         state = DFL;
  1671.         if(c == '\012'){
  1672.             GF_PUTC(f->next, '\015');
  1673.             GF_PUTC(f->next, '\012');
  1674.             f->n = 0L;
  1675.         }
  1676.         else{            /* nope, quote the CR */
  1677.             GF_8BIT_PUT_QUOTE(f, '\015');
  1678.             GF_8BIT_DEFAULT(f, c); /* and don't forget about c! */
  1679.         }
  1680.         break;
  1681.  
  1682.           case WSPACE:
  1683.         state = DFL;
  1684.         if(c == '\015' || f->t){ /* handle the space */
  1685.             GF_8BIT_PUT_QUOTE(f, ' ');
  1686.             f->t = 0;        /* reset From flag */
  1687.         }
  1688.         else
  1689.           GF_8BIT_PUT(f, ' ');
  1690.  
  1691.         GF_8BIT_DEFAULT(f, c);    /* handle 'c' in the default way */
  1692.         break;
  1693.         }
  1694.     }
  1695.  
  1696.     f->f1 = state;
  1697.     GF_END(f, f->next);
  1698.     }
  1699.     else if(flg == GF_EOD){
  1700.     switch(f->f1){
  1701.       case CCR :
  1702.         GF_8BIT_PUT_QUOTE(f, '\015'); /* write the last cr */
  1703.         break;
  1704.  
  1705.       case WSPACE :
  1706.         GF_8BIT_PUT_QUOTE(f, ' ');    /* write the last space */
  1707.         break;
  1708.     }
  1709.  
  1710.     GF_FLUSH(f->next);
  1711.     (*f->next->f)(f->next, GF_EOD);
  1712.     }
  1713.     else if(flg == GF_RESET){
  1714.     dprint(9, (debugfile, "-- gf_reset 8bit_qp\n"));
  1715.     f->f1 = DFL;            /* state from last character        */
  1716.     f->f2 = 1;            /* state of "^NFrom " bitmap        */
  1717.     f->t  = 0;
  1718.     f->n  = 0L;            /* number of chars in current line  */
  1719.     }
  1720. }
  1721.  
  1722.  
  1723.  
  1724. /*
  1725.  * RICHTEXT-TO-PLAINTEXT filter
  1726.  */
  1727.  
  1728. /*
  1729.  * option to be used by rich2plain (NOTE: if this filter is ever 
  1730.  * used more than once in a pipe, all instances will have the same
  1731.  * option value)
  1732.  */
  1733. static int gf_rich_plain = 0;
  1734.  
  1735.  
  1736. /*----------------------------------------------------------------------
  1737.       richtext to plaintext filter
  1738.     
  1739.  Args: f -- 
  1740.        flg  --
  1741.  
  1742.   This basically removes all richtext formatting. A cute hack is used 
  1743.   to get bold and underlining to work.
  1744.   Further work could be done to handle things like centering and right
  1745.   and left flush, but then it could no longer be done in place. This
  1746.   operates on text *with* CRLF's.
  1747.  
  1748.   WARNING: does not wrap lines!
  1749.  ----*/
  1750. void
  1751. gf_rich2plain(f, flg)
  1752.     FILTER_S *f;
  1753.     int       flg;
  1754. {
  1755. /* BUG: qoute incoming \255 values */
  1756.     GF_INIT(f, f->next);
  1757.  
  1758.     if(flg == GF_DATA){
  1759.     register unsigned char c;
  1760.     register int state = f->f1;
  1761.  
  1762.     while(GF_GETC(f, c)){
  1763.  
  1764.         switch(state){
  1765.           case TOKEN :        /* collect a richtext token */
  1766.         if(c == '>'){        /* what should we do with it? */
  1767.             state       = DFL;    /* return to default next time */
  1768.             *(f->linep) = '\0';    /* cap off token */
  1769.             if(f->line[0] == 'l' && f->line[1] == 't'){
  1770.             GF_PUTC(f->next, '<'); /* literal '<' */
  1771.             }
  1772.             else if(f->line[0] == 'n' && f->line[1] == 'l'){
  1773.             GF_PUTC(f->next, '\015');/* newline! */
  1774.             GF_PUTC(f->next, '\012');
  1775.             }
  1776.             else if(!strcmp("comment", f->line)){
  1777.             (f->f2)++;
  1778.             }
  1779.             else if(!strcmp("/comment", f->line)){
  1780.             f->f2 = 0;
  1781.             }
  1782.             else if(!strcmp("/paragraph", f->line)) {
  1783.             GF_PUTC(f->next, '\r');
  1784.             GF_PUTC(f->next, '\n');
  1785.             GF_PUTC(f->next, '\r');
  1786.             GF_PUTC(f->next, '\n');
  1787.             }
  1788.             else if(!gf_rich_plain){
  1789.             if(!strcmp(f->line, "bold")) {
  1790.                 GF_PUTC(f->next, TAG_EMBED);
  1791.                 GF_PUTC(f->next, TAG_BOLDON);
  1792.             } else if(!strcmp(f->line, "/bold")) {
  1793.                 GF_PUTC(f->next, TAG_EMBED);
  1794.                 GF_PUTC(f->next, TAG_BOLDOFF);
  1795.             } else if(!strcmp(f->line, "italic")) {
  1796.                 GF_PUTC(f->next, TAG_EMBED);
  1797.                 GF_PUTC(f->next, TAG_ULINEON);
  1798.             } else if(!strcmp(f->line, "/italic")) {
  1799.                 GF_PUTC(f->next, TAG_EMBED);
  1800.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1801.             } else if(!strcmp(f->line, "underline")) {
  1802.                 GF_PUTC(f->next, TAG_EMBED);
  1803.                 GF_PUTC(f->next, TAG_ULINEON);
  1804.             } else if(!strcmp(f->line, "/underline")) {
  1805.                 GF_PUTC(f->next, TAG_EMBED);
  1806.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1807.             } 
  1808.             }
  1809.             /* else we just ignore the token! */
  1810.  
  1811.             f->linep = f->line;    /* reset token buffer */
  1812.         }
  1813.         else{            /* add char to token */
  1814.             if(f->linep - f->line > 40){
  1815.             /* What? rfc1341 says 40 char tokens MAX! */
  1816.             fs_give((void **)&(f->line));
  1817.             gf_error("Richtext token over 40 characters");
  1818.             /* NO RETURN */
  1819.             }
  1820.         
  1821.             *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
  1822.         }
  1823.         break;
  1824.  
  1825.           case CCR   :
  1826.         state = DFL;        /* back to default next time */
  1827.         if(c == '\012'){    /* treat as single space?    */
  1828.             GF_PUTC(f->next, ' ');
  1829.             break;
  1830.         }
  1831.         /* fall thru to process c */
  1832.  
  1833.           case DFL   :
  1834.           default:
  1835.         if(c == '<')
  1836.           state = TOKEN;
  1837.         else if(c == '\015')
  1838.           state = CCR;
  1839.         else if(!f->f2)        /* not in comment! */
  1840.           GF_PUTC(f->next, c);
  1841.  
  1842.         break;
  1843.         }
  1844.     }
  1845.  
  1846.     f->f1 = state;
  1847.     GF_END(f, f->next);
  1848.     }
  1849.     else if(flg == GF_EOD){
  1850.     if(f->f1 = (f->linep != f->line)){
  1851.         /* incomplete token!! */
  1852.         gf_error("Incomplete token in richtext");
  1853.         /* NO RETURN */
  1854.     }
  1855.  
  1856.     fs_give((void **)&(f->line));
  1857.     GF_FLUSH(f->next);
  1858.     (*f->next->f)(f->next, GF_EOD);
  1859.     }
  1860.     else if(flg == GF_RESET){
  1861.     dprint(9, (debugfile, "-- gf_reset rich2plain\n"));
  1862.     f->f1 = DFL;            /* state */
  1863.     f->f2 = 0;            /* set means we're in a comment */
  1864.     f->linep = f->line = (char *)fs_get(45 * sizeof(char));
  1865.     }
  1866. }
  1867.  
  1868.  
  1869. /*
  1870.  * function called from the outside to set
  1871.  * richtext filter's options
  1872.  */
  1873. void
  1874. gf_rich2plain_opt(plain)
  1875.     int plain;
  1876. {
  1877.     gf_rich_plain = plain;
  1878. }
  1879.  
  1880.  
  1881.  
  1882. /*
  1883.  * ENRICHED-TO-PLAIN text filter
  1884.  */
  1885.  
  1886. static int gf_enriched_plain = 0;
  1887.  
  1888.  
  1889. /*----------------------------------------------------------------------
  1890.       enriched text to plain text filter (ala rfc1523)
  1891.     
  1892.  Args: f -- state and input data
  1893.        flg -- 
  1894.  
  1895.   This basically removes all enriched formatting. A cute hack is used 
  1896.   to get bold and underlining to work.
  1897.  
  1898.   Further work could be done to handle things like centering and right
  1899.   and left flush, but then it could no longer be done in place. This
  1900.   operates on text *with* CRLF's.
  1901.  
  1902.   WARNING: does not wrap lines!
  1903.  ----*/
  1904. void
  1905. gf_enriched2plain(f, flg)
  1906.     FILTER_S *f;
  1907.     int       flg;
  1908. {
  1909. /* BUG: qoute incoming \255 values */
  1910.     GF_INIT(f, f->next);
  1911.  
  1912.     if(flg == GF_DATA){
  1913.     register unsigned char c;
  1914.     register int state = f->f1;
  1915.  
  1916.     while(GF_GETC(f, c)){
  1917.  
  1918.         switch(state){
  1919.           case TOKEN :        /* collect a richtext token */
  1920.         if(c == '>'){        /* what should we do with it? */
  1921.             state       = DFL;    /* return to default next time */
  1922.             *(f->linep) = '\0';    /* cap off token */
  1923.             if(!strcmp("param", f->line)){
  1924.             (f->f2)++;
  1925.             }
  1926.             else if(!strcmp("/param", f->line)){
  1927.             f->f2 = 0;
  1928.             }
  1929.             else if(!gf_enriched_plain){
  1930.             /* Following is a cute hack or two to get 
  1931.                bold and underline on the screen. 
  1932.                See Putline0n() where these codes are
  1933.                interpreted */
  1934.             if(!strcmp(f->line, "bold")) {
  1935.                 GF_PUTC(f->next, TAG_EMBED);
  1936.                 GF_PUTC(f->next, TAG_BOLDON);
  1937.             } else if(!strcmp(f->line, "/bold")) {
  1938.                 GF_PUTC(f->next, TAG_EMBED);
  1939.                 GF_PUTC(f->next, TAG_BOLDOFF);
  1940.             } else if(!strcmp(f->line, "italic")) {
  1941.                 GF_PUTC(f->next, TAG_EMBED);
  1942.                 GF_PUTC(f->next, TAG_ULINEON);
  1943.             } else if(!strcmp(f->line, "/italic")) {
  1944.                 GF_PUTC(f->next, TAG_EMBED);
  1945.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1946.             } else if(!strcmp(f->line, "underline")) {
  1947.                 GF_PUTC(f->next, TAG_EMBED);
  1948.                 GF_PUTC(f->next, TAG_ULINEON);
  1949.             } else if(!strcmp(f->line, "/underline")) {
  1950.                 GF_PUTC(f->next, TAG_EMBED);
  1951.                 GF_PUTC(f->next, TAG_ULINEOFF);
  1952.             } 
  1953.             }
  1954.             /* else we just ignore the token! */
  1955.  
  1956.             f->linep = f->line;    /* reset token buffer */
  1957.         }
  1958.         else if(c == '<'){        /* literal '<'? */
  1959.             if(f->linep == f->line){
  1960.             GF_PUTC(f->next, '<');
  1961.             state = DFL;
  1962.             }
  1963.             else{
  1964.             fs_give((void **)&(f->line));
  1965.             gf_error("Malformed Enriched text: unexpected '<'");
  1966.             /* NO RETURN */
  1967.             }
  1968.         }
  1969.         else{            /* add char to token */
  1970.             if(f->linep - f->line > 60){ /* rfc1523 says 60 MAX! */
  1971.             fs_give((void **)&(f->line));
  1972.             gf_error("Malformed Enriched text: token too long");
  1973.             /* NO RETURN */
  1974.             }
  1975.         
  1976.             *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
  1977.         }
  1978.         break;
  1979.  
  1980.           case CCR   :
  1981.         if(c != '\012'){    /* treat as single space?    */
  1982.             state = DFL;    /* lone cr? */
  1983.             f->f2 = 0;
  1984.             GF_PUTC(f->next, '\015');
  1985.             goto df;
  1986.         }
  1987.  
  1988.         state = CLF;
  1989.         break;
  1990.  
  1991.           case CLF   :
  1992.         if(c == '\015'){    /* treat as single space?    */
  1993.             state = CCR;    /* repeat crlf's mean real newlines */
  1994.             f->f2 = 1;
  1995.             GF_PUTC(f->next, '\r');
  1996.             GF_PUTC(f->next, '\n');
  1997.             break;
  1998.         }
  1999.         else{
  2000.             state = DFL;
  2001.             if(!f->f2)
  2002.               GF_PUTC(f->next, ' ');
  2003.  
  2004.             f->f2 = 0;
  2005.         }
  2006.  
  2007.         /* fall thru to take care of 'c' */
  2008.  
  2009.           case DFL   :
  2010.           default :
  2011.           df : 
  2012.         if(c == '<')
  2013.           state = TOKEN;
  2014.         else if(c == '\015')
  2015.           state = CCR;
  2016.         else if(!f->f2)        /* not in param! */
  2017.           GF_PUTC(f->next, c);
  2018.  
  2019.         break;
  2020.         }
  2021.     }
  2022.  
  2023.     f->f1 = state;
  2024.     GF_END(f, f->next);
  2025.     }
  2026.     else if(flg == GF_EOD){
  2027.     if(f->f1 = (f->linep != f->line)){
  2028.         /* incomplete token!! */
  2029.         gf_error("Incomplete token in richtext");
  2030.         /* NO RETURN */
  2031.     }
  2032.  
  2033.     fs_give((void **)&(f->line));
  2034.  
  2035.     GF_FLUSH(f->next);
  2036.     (*f->next->f)(f->next, GF_EOD);
  2037.     }
  2038.     else if(flg == GF_RESET){
  2039.     dprint(9, (debugfile, "-- gf_reset enriched2plain\n"));
  2040.     f->f1 = DFL;            /* state */
  2041.     f->f2 = 0;            /* set means we're in a comment */
  2042.     f->linep = f->line = (char *)fs_get(65 * sizeof(char));
  2043.     }
  2044. }
  2045.  
  2046.  
  2047. /*
  2048.  * function called from the outside to set
  2049.  * richtext filter's options
  2050.  */
  2051. void
  2052. gf_enriched2plain_opt(plain)
  2053.     int plain;
  2054. {
  2055.     gf_enriched_plain = plain;
  2056. }
  2057.  
  2058.  
  2059.  
  2060. /*
  2061.  * HTML-TO-PLAIN text filter
  2062.  */
  2063.  
  2064. /*----------------------------------------------------------------------
  2065.   HTML text to plain text filter (ala HTML 2.0)
  2066.     
  2067.  
  2068.   This basically tries to do the best it can with HTML 2.0, level 1
  2069.   text formatting.
  2070.  
  2071.  ----*/
  2072. void
  2073. gf_html2plain(f, flg)
  2074.     FILTER_S *f;
  2075.     int       flg;
  2076. {
  2077.     /* place holder for filter under development */
  2078. }
  2079.  
  2080.  
  2081. /*
  2082.  * ESCAPE CODE FILTER - remove unknown and possibly dangerous escape codes
  2083.  * from the text stream.
  2084.  */
  2085.  
  2086. #define    MAX_ESC_LEN    5
  2087.  
  2088. /*
  2089.  * the simple filter, removes unknown escape codes from the stream
  2090.  */
  2091. void
  2092. gf_escape_filter(f, flg)
  2093.     FILTER_S *f;
  2094.     int       flg;
  2095. {
  2096.     register char *p;
  2097.     GF_INIT(f, f->next);
  2098.  
  2099.     if(flg == GF_DATA){
  2100.     register unsigned char c;
  2101.     register int state = f->f1;
  2102.  
  2103.     while(GF_GETC(f, c)){
  2104.  
  2105.         if(state){
  2106.         if(c == '\033' || f->n == MAX_ESC_LEN){
  2107.             f->line[f->n] = '\0';
  2108.             f->n = 0L;
  2109.             if(!match_escapes(f->line)){
  2110.             GF_PUTC(f->next, '^');
  2111.             GF_PUTC(f->next, '[');
  2112.             }
  2113.             else
  2114.               GF_PUTC(f->next, '\033');
  2115.  
  2116.             p = f->line;
  2117.             while(*p)
  2118.               GF_PUTC(f->next, *p++);
  2119.  
  2120.             if(c == '\033')
  2121.               continue;
  2122.             else
  2123.               state = 0;            /* fall thru */
  2124.         }
  2125.         else{
  2126.             f->line[f->n++] = c;        /* collect */
  2127.             continue;
  2128.         }
  2129.         }
  2130.  
  2131.         if(c == '\033')
  2132.           state = 1;
  2133.         else
  2134.           GF_PUTC(f->next, c);
  2135.     }
  2136.  
  2137.     f->f1 = state;
  2138.     GF_END(f, f->next);
  2139.     }
  2140.     else if(flg == GF_EOD){
  2141.     if(f->f1){
  2142.         if(!match_escapes(f->line)){
  2143.         GF_PUTC(f->next, '^');
  2144.         GF_PUTC(f->next, '[');
  2145.         }
  2146.         else
  2147.           GF_PUTC(f->next, '\033');
  2148.     }
  2149.  
  2150.     for(p = f->line; f->n; f->n--, p++)
  2151.       GF_PUTC(f->next, *p);
  2152.  
  2153.     fs_give((void **)&(f->line));    /* free temp line buffer */
  2154.     GF_FLUSH(f->next);
  2155.     (*f->next->f)(f->next, GF_EOD);
  2156.     }
  2157.     else if(flg == GF_RESET){
  2158.     dprint(9, (debugfile, "-- gf_reset escape\n"));
  2159.     f->f1    = 0;
  2160.     f->n     = 0L;
  2161.     f->linep = f->line = (char *)fs_get((MAX_ESC_LEN + 1) * sizeof(char));
  2162.     }
  2163. }
  2164.  
  2165.  
  2166.  
  2167. /*
  2168.  * CONTROL CHARACTER FILTER - transmogrify control characters into their
  2169.  * corresponding string representations (you know, ^blah and such)...
  2170.  */
  2171.  
  2172. /*
  2173.  * the simple filter transforms unknown control characters in the stream
  2174.  * into harmless strings.
  2175.  */
  2176. void
  2177. gf_control_filter(f, flg)
  2178.     FILTER_S *f;
  2179.     int       flg;
  2180. {
  2181.     register char *p;
  2182.     GF_INIT(f, f->next);
  2183.  
  2184.     if(flg == GF_DATA){
  2185.     register unsigned char c;
  2186.  
  2187.     while(GF_GETC(f, c)){
  2188.  
  2189.         if(iscntrl(c & 0x7f)
  2190.            && !(isspace((unsigned char)c)
  2191.             || c == '\016' || c == '\017' || c == '\033')){
  2192.         GF_PUTC(f->next, '^');
  2193.         GF_PUTC(f->next, c + '@');
  2194.         }
  2195.         else
  2196.           GF_PUTC(f->next, c);
  2197.     }
  2198.  
  2199.     GF_END(f, f->next);
  2200.     }
  2201.     else if(flg == GF_EOD){
  2202.     GF_FLUSH(f->next);
  2203.     (*f->next->f)(f->next, GF_EOD);
  2204.     }
  2205. }
  2206.  
  2207.  
  2208.  
  2209. /*
  2210.  * LINEWRAP FILTER - insert CRLF's at end of nearest whitespace before
  2211.  * specified line width
  2212.  */
  2213.  
  2214. /*
  2215.  * option to be used by gf_wrap_filter (NOTE: if this filter is ever 
  2216.  * used more than once in a pipe, all instances will have the same
  2217.  * option value)
  2218.  */
  2219. static int gf_wrap_width = 75;
  2220.  
  2221.  
  2222. /*
  2223.  * the simple filter, breaks lines at end of white space nearest
  2224.  * to global "gf_wrap_width" in length
  2225.  */
  2226. void
  2227. gf_wrap(f, flg)
  2228.     FILTER_S *f;
  2229.     int       flg;
  2230. {
  2231.     register long i;
  2232.     register char *breakp, *tp;
  2233.     GF_INIT(f, f->next);
  2234.  
  2235.     if(flg == GF_DATA){
  2236.     register unsigned char c;
  2237.     register int state = f->f1;
  2238.  
  2239.     while(GF_GETC(f, c)){
  2240.  
  2241.         switch(state){
  2242.           case CCR:
  2243.         state = DFL;
  2244.         if(c == '\012'){
  2245.             for(tp = f->line; tp < f->linep; tp++)
  2246.               GF_PUTC(f->next, *tp);
  2247.  
  2248.             GF_PUTC(f->next, '\015');
  2249.             GF_PUTC(f->next, '\012');
  2250.             f->n = 0L;
  2251.             f->linep = f->line;
  2252.             break;
  2253.         }
  2254.         else{
  2255.             *(f->linep)++ = '\015';    /* shouldn't happen often! */
  2256.             (f->n)++;
  2257.         }
  2258.         /* else fall thru to take care of c */
  2259.  
  2260.           case DFL:
  2261.         if(c == '\015'){        /* already has newline? */
  2262.             state = CCR;
  2263.             break;
  2264.         }
  2265.         else if(c == '\011'){    /* account for tabs too! */
  2266.             i = f->n;
  2267.             while((++i)&0x07)
  2268.               ;
  2269.  
  2270.             i -= f->n;
  2271.         }
  2272.         else
  2273.           i = 1;
  2274.  
  2275.         if(f->n + i > (long)gf_wrap_width){ /* wrap? */
  2276.             for(breakp = &f->linep[-1]; breakp >= f->line; breakp--)
  2277.               if(isspace((unsigned char)*breakp))
  2278.             break;
  2279.  
  2280.             f->n = i = 0;
  2281.             for(tp = f->line; tp < f->linep; tp++){
  2282.             if(breakp < f->line || tp <= breakp)
  2283.               GF_PUTC(f->next, *tp); /* write the line */
  2284.             else{        /* shift it back */
  2285.                 i = tp - breakp - 1;
  2286.                 if((f->line[i++] = *tp) == '\011')
  2287.                   while((++(f->n))&0x07);
  2288.                 else
  2289.                   (f->n)++;
  2290.             }
  2291.             }
  2292.  
  2293.             GF_PUTC(f->next, '\015');
  2294.             GF_PUTC(f->next, '\012');
  2295.             f->linep = &f->line[i];    /* reset f->linep */
  2296.         }
  2297.  
  2298.         if((*(f->linep)++ = c) == '\011')
  2299.           while((++(f->n))&0x07);
  2300.         else
  2301.           (f->n)++;
  2302.  
  2303.         break;
  2304.         }
  2305.     }
  2306.  
  2307.     f->f1 = state;
  2308.     GF_END(f, f->next);
  2309.     }
  2310.     else if(flg == GF_EOD){
  2311.     for(i = 0; i < f->n; i++)    /* flush the remaining line */
  2312.       GF_PUTC(f->next, f->line[i]);
  2313.  
  2314.     fs_give((void **)&(f->line));    /* free temp line buffer */
  2315.     GF_FLUSH(f->next);
  2316.     (*f->next->f)(f->next, GF_EOD);
  2317.     }
  2318.     else if(flg == GF_RESET){
  2319.     dprint(9, (debugfile, "-- gf_reset wrap\n"));
  2320.     f->f1    = DFL;
  2321.     f->n     = 0L;
  2322.     f->linep = f->line = (char *)fs_get((gf_wrap_width+10)*sizeof(char));
  2323.     }
  2324. }
  2325.  
  2326.  
  2327. /*
  2328.  * function called from the outside to set
  2329.  * wrap filter's width option
  2330.  */
  2331. void
  2332. gf_wrap_filter_opt(width)
  2333.     int width;
  2334. {
  2335.     gf_wrap_width = width;
  2336. }
  2337.  
  2338. /*
  2339.  * LINE PREFIX FILTER - insert given text at beginning of each
  2340.  * line
  2341.  */
  2342.  
  2343. /*
  2344.  * option to be used by gf_prefix
  2345.  */
  2346. static char *gf_prefix_prefix = NULL;
  2347.  
  2348. #define    GF_PREFIX_WRITE(s)    { \
  2349.                     register char *p; \
  2350.                     if(p = (s)) \
  2351.                       while(*p) \
  2352.                     GF_PUTC(f->next, *p++); \
  2353.                 }
  2354.  
  2355.  
  2356. /*
  2357.  * the simple filter, prepends each line with the requested prefix.
  2358.  * if prefix is null, does nothing, and as with all filters, assumes
  2359.  * NVT end of lines.
  2360.  */
  2361. void
  2362. gf_prefix(f, flg)
  2363.     FILTER_S *f;
  2364.     int       flg;
  2365. {
  2366.     GF_INIT(f, f->next);
  2367.  
  2368.     if(flg == GF_DATA){
  2369.     register unsigned char c;
  2370.     register int state = f->f1;
  2371.     register int first = f->f2;
  2372.  
  2373.     while(GF_GETC(f, c)){
  2374.  
  2375.         if(first){            /* write initial prefix!! */
  2376.         first = 0;        /* but just once */
  2377.         GF_PREFIX_WRITE((char *) f->data);
  2378.         }
  2379.         else if(state){
  2380.         state = 0;
  2381.         GF_PUTC(f->next, '\015');
  2382.         if(c == '\012'){
  2383.             GF_PUTC(f->next, '\012');
  2384.             GF_PREFIX_WRITE((char *) f->data);
  2385.             continue;
  2386.         }
  2387.         /* else fall thru to handle 'c' */
  2388.         }
  2389.  
  2390.         if(c == '\015')        /* already has newline? */
  2391.           state = 1;
  2392.         else
  2393.           GF_PUTC(f->next, c);
  2394.     }
  2395.  
  2396.     f->f1 = state;
  2397.     f->f2 = first;
  2398.     GF_END(f, f->next);
  2399.     }
  2400.     else if(flg == GF_EOD){
  2401.     GF_FLUSH(f->next);
  2402.     (*f->next->f)(f->next, GF_EOD);
  2403.     }
  2404.     else if(flg == GF_RESET){
  2405.     dprint(9, (debugfile, "-- gf_reset prefix\n"));
  2406.     f->f1   = 0;
  2407.     f->f2   = 1;            /* nothing written yet */
  2408.     f->data = gf_prefix_prefix;
  2409.     }
  2410. }
  2411.  
  2412.  
  2413. /*
  2414.  * function called from the outside to set
  2415.  * prefix filter's prefix string
  2416.  */
  2417. void
  2418. gf_prefix_opt(prefix)
  2419.     char *prefix;
  2420. {
  2421.     gf_prefix_prefix = prefix;
  2422. }
  2423.  
  2424.  
  2425. /*
  2426.  * LINE TEST FILTER - accumulate lines and offer each to the provided
  2427.  * test function.
  2428.  */
  2429.  
  2430.  
  2431. /*
  2432.  * option to be used by gf_line_test
  2433.  */
  2434. static int (*gf_line_test_f) PROTO((long, char *));
  2435.  
  2436. /* accumulator growth increment */
  2437. #define    LINE_TEST_BLOCK    1024
  2438.  
  2439. #define    GF_LINE_TEST_EOB(f)    ((f)->line + ((f)->f2 - 1))
  2440.  
  2441. #define    GF_LINE_TEST_FLUSH(f)    { \
  2442.                     register char *op; \
  2443.                     for(op = (f)->line; op < p; op++) \
  2444.                       GF_PUTC((f)->next, *op); \
  2445.                     p = (f)->line; \
  2446.                 }
  2447.  
  2448.  
  2449. #define    GF_LINE_TEST_ADD(f, c)    { \
  2450.                     if(p >= eobuf){ \
  2451.                     f->f2 += LINE_TEST_BLOCK; \
  2452.                     fs_resize((void **)&f->line, \
  2453.                           (size_t) f->f2 * sizeof(char)); \
  2454.                     eobuf = GF_LINE_TEST_EOB(f); \
  2455.                     p = eobuf - LINE_TEST_BLOCK; \
  2456.                     } \
  2457.                     *p++ = c; \
  2458.                 }
  2459.  
  2460.  
  2461. /*
  2462.  * this simple filter accumulates characters until a newline, offers it
  2463.  * to the provided test function, and then passes it on.  It assumes
  2464.  * NVT EOLs.
  2465.  */
  2466. void
  2467. gf_line_test(f, flg)
  2468.     FILTER_S *f;
  2469.     int          flg;
  2470. {
  2471.     register char *p = f->linep;
  2472.     register char *eobuf = GF_LINE_TEST_EOB(f);
  2473.     GF_INIT(f, f->next);
  2474.  
  2475.     if(flg == GF_DATA){
  2476.     register unsigned char c;
  2477.     register int state = f->f1;
  2478.  
  2479.     while(GF_GETC(f, c)){
  2480.  
  2481.         if(state){
  2482.         state = 0;
  2483.         if(c == '\012'){
  2484.             int done;
  2485.  
  2486.             *p = '\0';
  2487.             done = (*((int (*)()) f->data))(f->n++, f->line);
  2488.             GF_LINE_TEST_FLUSH(f);
  2489.             GF_PUTC(f->next, '\015');
  2490.             GF_PUTC(f->next, '\012');
  2491.             /*
  2492.              * if the line tester returns TRUE, it's
  2493.              * telling us its seen enough and doesn't
  2494.              * want to see any more.  Remove ourself 
  2495.              * from the pipeline...
  2496.              */
  2497.             if(done){
  2498.             if(gf_master == f){
  2499.                 gf_master = f->next;
  2500.             }
  2501.             else{
  2502.                 FILTER_S *fprev;
  2503.  
  2504.                 for(fprev = gf_master;
  2505.                 fprev && fprev->next != f;
  2506.                 fprev = fprev->next)
  2507.                   ;
  2508.  
  2509.                 if(fprev)        /* wha??? */
  2510.                   fprev->next = f->next;
  2511.                 else
  2512.                   continue;
  2513.             }
  2514.  
  2515.             while(GF_GETC(f, c))    /* pass input */
  2516.               GF_PUTC(f->next, c);
  2517.  
  2518.             GF_FLUSH(f->next);    /* and drain queue */
  2519.             fs_give((void **)&f->line);
  2520.             fs_give((void **)&f);    /* wax our data */
  2521.             return;
  2522.             }
  2523.             else
  2524.               continue;
  2525.         }
  2526.         else            /* add CR to buffer */
  2527.           GF_LINE_TEST_ADD(f, '\015');
  2528.         } /* fall thru to handle 'c' */
  2529.  
  2530.         if(c == '\015')        /* newline? */
  2531.           state = 1;
  2532.         else
  2533.           GF_LINE_TEST_ADD(f, c);
  2534.     }
  2535.  
  2536.     f->f1 = state;
  2537.     GF_END(f, f->next);
  2538.     }
  2539.     else if(flg == GF_EOD){
  2540.     GF_LINE_TEST_FLUSH(f);        /* BUG: should pass to test func? */
  2541.     fs_give((void **)&f->line);
  2542.     GF_FLUSH(f->next);
  2543.     (*f->next->f)(f->next, GF_EOD);
  2544.     }
  2545.     else if(flg == GF_RESET){
  2546.     dprint(9, (debugfile, "-- gf_reset line_test\n"));
  2547.     f->f1 = 0;            /* state */
  2548.     f->n  = 0L;            /* line number */
  2549.     f->f2 = LINE_TEST_BLOCK;    /* size of alloc'd line */
  2550.     f->line = p = (char *) fs_get(f->f2 * sizeof(char));
  2551.     f->data = (void *)gf_line_test_f;
  2552.     }
  2553.  
  2554.     f->linep = p;
  2555. }
  2556.  
  2557.  
  2558. /*
  2559.  * function called from the outside to operate on accumulated line.
  2560.  */
  2561. void
  2562. gf_line_test_opt(test_f)
  2563.     int  (*test_f) PROTO((long, char *));
  2564. {
  2565.     gf_line_test_f = test_f;
  2566. }
  2567.  
  2568.  
  2569. /*
  2570.  * Network virtual terminal to local newline convention filter
  2571.  */
  2572. void
  2573. gf_nvtnl_local(f, flg)
  2574.     FILTER_S *f;
  2575.     int       flg;
  2576. {
  2577.     GF_INIT(f, f->next);
  2578.  
  2579.     if(flg == GF_DATA){
  2580.     register unsigned char c;
  2581.     register int state = f->f1;
  2582.  
  2583.     while(GF_GETC(f, c)){
  2584.  
  2585. #ifdef    CRLF_NEWLINES
  2586.         /*
  2587.          * NOOP!
  2588.          */
  2589.         GF_PUTC(f->next, c);
  2590. #else
  2591.         if(state){
  2592.         state = 0;
  2593.         if(c == '\012'){
  2594.             GF_PUTC(f->next, '\012');
  2595.             continue;
  2596.         }
  2597.         else
  2598.           GF_PUTC(f->next, '\015');
  2599.         /* fall thru to deal with 'c' */
  2600.         }
  2601.  
  2602.         if(c == '\015')
  2603.           state = 1;
  2604.         else
  2605.           GF_PUTC(f->next, c);
  2606. #endif
  2607.     }
  2608.  
  2609.     f->f1 = state;
  2610.     GF_END(f, f->next);
  2611.     }
  2612.     else if(flg == GF_EOD){
  2613.     GF_FLUSH(f->next);
  2614.     (*f->next->f)(f->next, GF_EOD);
  2615.     }
  2616.     else if(flg == GF_RESET){
  2617.     dprint(9, (debugfile, "-- gf_reset nvtnl_local\n"));
  2618.     f->f1 = 0;
  2619.     }
  2620. }
  2621.  
  2622.  
  2623. /*
  2624.  * local to network newline convention filter
  2625.  */
  2626. void
  2627. gf_local_nvtnl(f, flg)
  2628.     FILTER_S *f;
  2629.     int       flg;
  2630. {
  2631.     GF_INIT(f, f->next);
  2632.  
  2633.     if(flg == GF_DATA){
  2634.     register unsigned char c;
  2635.  
  2636.     while(GF_GETC(f, c)){
  2637.  
  2638. #ifdef    CRLF_NEWLINES
  2639.         /*
  2640.          * NOOP!
  2641.          */
  2642.         GF_PUTC(f->next, c);
  2643. #else
  2644.         if(c == '\012'){
  2645.         GF_PUTC(f->next, '\015');
  2646.         GF_PUTC(f->next, '\012');
  2647.         }
  2648.         else
  2649.           GF_PUTC(f->next, c);
  2650. #endif
  2651.     }
  2652.  
  2653.     GF_END(f, f->next);
  2654.     }
  2655.     else if(flg == GF_EOD){
  2656.     GF_FLUSH(f->next);
  2657.     (*f->next->f)(f->next, GF_EOD);
  2658.     }
  2659.     else if(GF_RESET){
  2660.     dprint(9, (debugfile, "-- gf_reset local_nvtnl\n"));
  2661.     /* no op */
  2662.     }
  2663.  
  2664. }
  2665.  
  2666. #if defined(DOS) || defined(OS2)
  2667. /*
  2668.  * DOS CodePage to Character Set Translation (and back) filters
  2669.  */
  2670.  
  2671. /*
  2672.  * Charset and CodePage mapping table pointer and length
  2673.  */
  2674. static unsigned char *gf_xlate_tab;
  2675. static unsigned gf_xlate_tab_len;
  2676.  
  2677. /*
  2678.  * the simple filter takes DOS Code Page values and maps them into
  2679.  * the indicated external CharSet mapping or vice-versa.
  2680.  */
  2681. void
  2682. gf_translate(f, flg)
  2683.     FILTER_S *f;
  2684.     int       flg;
  2685. {
  2686.     GF_INIT(f, f->next);
  2687.  
  2688.     if(flg == GF_DATA){
  2689.     register unsigned char c;
  2690.  
  2691.     while(GF_GETC(f, c))
  2692.       if((unsigned) c < gf_xlate_tab_len)
  2693.         GF_PUTC(f->next, (int)gf_xlate_tab[c]);
  2694.  
  2695.     GF_END(f, f->next);
  2696.     }
  2697.     else if(flg == GF_EOD){
  2698.     GF_FLUSH(f->next);
  2699.     (*f->next->f)(f->next, GF_EOD);
  2700.     }
  2701.     else if(GF_RESET){
  2702.     dprint(9, (debugfile, "-- gf_reset translate\n"));
  2703.     /* no op */
  2704.     }
  2705. }
  2706.  
  2707.  
  2708. /*
  2709.  * function called from the outside to set
  2710.  * prefix filter's prefix string
  2711.  */
  2712. void
  2713. gf_translate_opt(xlatetab, xlatetablen)
  2714.     unsigned char *xlatetab;
  2715.     unsigned       xlatetablen;
  2716. {
  2717.     gf_xlate_tab     = xlatetab;
  2718.     gf_xlate_tab_len = xlatetablen;
  2719. }
  2720. #endif
  2721.  
  2722. /*
  2723.  * display something indicating we're chewing on something
  2724.  *
  2725.  * NOTE : IF ANY OTHER FILTERS WRITE THE DISPLAY, THIS WILL NEED FIXING
  2726.  */
  2727. void
  2728. gf_busy(f, flg)
  2729.     FILTER_S *f;
  2730.     int       flg;
  2731. {
  2732.     static short x = 0;
  2733.     GF_INIT(f, f->next);
  2734.  
  2735.     if(flg == GF_DATA){
  2736.     register unsigned char c;
  2737.  
  2738.     while(GF_GETC(f, c)){
  2739.  
  2740.         if(!((++(f->f1))&0x7ff)){     /* ding the bell every 2K chars */
  2741.         MoveCursor(0, 1);
  2742.         f->f1 = 0;
  2743.         if((++x)&0x04) x = 0;
  2744.         Writechar((x == 0) ? '/' :     /* CHEATING! */
  2745.               (x == 1) ? '-' : 
  2746.               (x == 2) ? '\\' : '|', 0);
  2747.         }
  2748.  
  2749.         GF_PUTC(f->next, c);
  2750.     }
  2751.  
  2752.     GF_END(f, f->next);
  2753.     }
  2754.     else if(flg == GF_EOD){
  2755.     MoveCursor(0, 1);
  2756.     Writechar(' ', 0);
  2757.     EndInverse();
  2758.     GF_FLUSH(f->next);
  2759.     (*f->next->f)(f->next, GF_EOD);
  2760.     }
  2761.     else if(flg == GF_RESET){
  2762.     dprint(9, (debugfile, "-- gf_reset busy\n"));
  2763.     f->f1 = 0;
  2764.         x = 0;
  2765.     StartInverse();
  2766.     }
  2767.  
  2768.     fflush(stdout);
  2769. }
  2770.